package com.yf.modules.system.service.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.ObjectUtil;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.yf.Utils.CacheKey;
import com.yf.Utils.FileUtil;
import com.yf.Utils.RedisUtils;
import com.yf.dao.RoleDeptMapper;
import com.yf.dao.RoleMenuMapper;
import com.yf.dao.UserMapper;
import com.yf.domain.security.Role;
import com.yf.domain.security.User;
import com.yf.model.query.RoleQueryCriteria;
import com.yf.modules.system.manager.UserCacheManager;
import com.yf.modules.system.model.dto.RoleSmallDto;
import com.yf.modules.system.service.api.RoleService;
import com.yf.dao.RoleMapper;
import com.yf.response.exceptions.BusinessException;
import jakarta.servlet.http.HttpServletResponse;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.io.IOException;
import java.util.*;
import java.util.stream.Collectors;

/**
 * @author chentianwei
 * @date 2024-01-25 15:42
 * @description Role服务实现曾
 */

@Service
@Slf4j
public class RoleServiceImpl extends ServiceImpl<RoleMapper, Role> implements RoleService {

    private final RoleMapper roleMapper;
    private final UserMapper userMapper;
    private final RedisUtils redisUtils;
    private final RoleDeptMapper roleDeptMapper;

    private final RoleMenuMapper roleMenuMapper;

    private final UserCacheManager userCacheManager;

    public RoleServiceImpl(RoleMapper roleMapper,
                           UserMapper userMapper,
                           RedisUtils redisUtils,
                           RoleDeptMapper roleDeptMapper,
                           RoleMenuMapper roleMenuMapper,
                           UserCacheManager userCacheManager) {
        this.roleMapper = roleMapper;
        this.userMapper = userMapper;
        this.redisUtils = redisUtils;
        this.roleDeptMapper = roleDeptMapper;
        this.roleMenuMapper = roleMenuMapper;
        this.userCacheManager = userCacheManager;
    }

    /**
     * 通过用户ID获取用户下所有的角色
     *
     * @param id 用户的唯一主键
     * @return 该用户下挂的角色
     */
    @Override
    public List<RoleSmallDto> findRolesByUserId(Long id) {
        Set<Role> roles = roleMapper.findRolesByUserId(id);
        return RoleSmallDto.convertToRoleSmallDto(roles);
    }

    /**
     * 根据菜单Id查询
     *
     * @param menuId /
     * @return /
     */
    @Override
    public List<Role> findByMenuId(Long menuId) {
        return roleMapper.findByMenuId(menuId);
    }

    /**
     * 查询角色信息
     *
     * @param criteria 查询条件
     * @return 角色集合
     */
    @Override
    public List<Role> queryRole(RoleQueryCriteria criteria) {
        return roleMapper.findAll(criteria);
    }

    /**
     * 新增角色
     *
     * @param role 实体
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void addRole(Role role) {
        if (ObjectUtil.isNotEmpty(role.getId())) {
            throw new BusinessException(role.getName() + "已有Id");
        }
        //保存
        save(role);
        //创建角色-部门关联
        if (CollectionUtil.isNotEmpty(role.getDepartments())) {
            roleDeptMapper.insertData(role.getId(), role.getDepartments());
        }
    }

    /**
     * 编辑角色
     *
     * @param role 实体
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void editRole(Role role) {
        Role entity = getById(role.getId());
        Role role1 = roleMapper.findByName(role.getName());
        if (role1 != null && !role1.getId().equals(entity.getId())) {
            throw new BusinessException(role.getName() + "已存在,无法修改");
        }
        entity.setName(role.getName());
        entity.setDescription(role.getDescription());
        entity.setDataScope(role.getDataScope());
        entity.setDepartments(role.getDepartments());
        entity.setLevel(role.getLevel());
        // 更新
        saveOrUpdate(entity);
        // 删除关联部门数据
        roleDeptMapper.deleteByRoleId(role.getId());
        // 判断是否有部门数据，若有，则需更新关联
        if (CollectionUtil.isNotEmpty(role.getDepartments())) {
            roleDeptMapper.insertData(role.getId(), role.getDepartments());
        }
        // 更新相关缓存
        delCaches(role.getId(), null);
    }

    /**
     * 删除角色
     *
     * @param ids 主键
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void deleteRole(Set<Long> ids) {
        //先验证是否被关联
        verification(ids);
        for (Long id : ids) {
            // 更新相关缓存
            delCaches(id, null);
        }
        removeBatchByIds(ids);
        // 删除角色部门关联数据、角色菜单关联数据
        roleDeptMapper.deleteByRoleIds(ids);
        roleMenuMapper.deleteByRoleIds(ids);
    }

    /**
     * 验证是否被用户关联
     *
     * @param ids 主键
     */
    @Override
    public void verification(Set<Long> ids) {
        if (userMapper.countByRoles(ids) > 0) {
            throw new BusinessException("所选角色存在用户关联，请解除关联再试！");
        }
    }

    /**
     * 下载角色信息
     *
     * @param response servlet返回体
     * @param criteria 查询条件
     */
    @Override
    public void download(HttpServletResponse response, RoleQueryCriteria criteria) throws IOException {
        List<Role> roleList = queryRole(criteria);
        List<Map<String, Object>> list = new ArrayList<>();
        for (Role role : roleList) {
            Map<String, Object> map = new LinkedHashMap<>();
            map.put("角色名称", role.getName());
            map.put("角色级别", role.getLevel());
            map.put("描述", role.getDescription());
            map.put("创建日期", role.getCreateTime());
            list.add(map);
        }
        FileUtil.downloadExcel(list, response);
    }

    /**
     * 关联角色-菜单
     *
     * @param role 角色
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public void linkRoleMenu(Role role) {
        List<User> users = userMapper.findByRoleId(role.getId());
        // 更新菜单
        roleMenuMapper.deleteByRoleId(role.getId());
        // 判断是否为空
        if (CollUtil.isNotEmpty(role.getMenus())) {
            roleMenuMapper.insertData(role.getId(), role.getMenus());
        }
        // 更新缓存
        delCaches(role.getId(), users);
    }

    /**
     * 清理缓存
     *
     * @param id /
     */
    public void delCaches(Long id, List<User> users) {
        users = CollectionUtil.isEmpty(users) ? userMapper.findByRoleId(id) : users;
        if (CollectionUtil.isNotEmpty(users)) {
            users.forEach(item -> userCacheManager.cleanUserCache(item.getUserName()));
            Set<Long> userIds = users.stream().map(User::getId).collect(Collectors.toSet());
            redisUtils.delByKeys(CacheKey.DATA_USER, userIds);
            redisUtils.delByKeys(CacheKey.MENU_USER, userIds);
            redisUtils.delByKeys(CacheKey.ROLE_AUTH, userIds);
        }
        redisUtils.del(CacheKey.ROLE_ID + id);
    }
}
